#include <iostream.h>
#include <fstream.h>
#include <iomanip.h>
#include <stdlib.h>
#include <string>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <math.h>

#include "Dog.h"

using std::string;

using namespace std;

/* -- GLOBAL VARIABLES --------------------------------------------------- */

//radious of the leg
float rCylinder = 3;

//initial spin value
static GLfloat spin = -30.0;
//initial position value
static GLfloat position = 0.0;
//indicate if the animation reaches an end
static int End = 0;

void Dog::printD()
{
	cout << "printD"<<endl;
}

/*!
	\draw a sphere
	\param r radius
	\param divY latituted division
	\param divX lognitude division
*/
void Dog::DrawSphere(double r, int divX, int divY) {
    int i, j;
    for(i = 0; i <= divY; i++) {
        double lat0 = M_PI * (-0.5 + (double) (i - 1) / divY);
        double z0  = r * sin(lat0);
        double zr0 =  r * cos(lat0);

        double lat1 = M_PI * (-0.5 + (double) i / divY);
        double z1 = r * sin(lat1);
        double zr1 = r * cos(lat1);

        glBegin(GL_QUAD_STRIP);
        for(j = 0; j <= divX; j++) {
            double lng = 2 * M_PI * (double) (j - 1) / divX;
            double x = cos(lng);
            double y = sin(lng);

            glNormal3f(x * zr0, y * zr0, z0);
            glVertex3f(x * zr0, y * zr0, z0);
            glNormal3f(x * zr1, y * zr1, z1);
            glVertex3f(x * zr1, y * zr1, z1);
        }
        glEnd();
    }
}

/*!
	\draw a cylinder
	\param r radius
	\param divY latituted division
	\param divX lognitude division
*/
void Dog :: DrawCylinder(float base, float top, float height,int divX, int divY)
{
	int i, j;
	// int divY = 100;                // Indicates the division around y-axiz(slice)
	// int divX = 100;                // Indicates the division along y-axiz(stack)
	// GLfloat base = 0.8;
	// GLfloat top = 0.8;
	// GLfloat height = 8;
	GLfloat v[divX * divY][3];
	float ratio;
	ratio = (base-top)/ height; 
	for (i = 0; i < divX; i++){
		   for (j = 0; j < divY; j++){
				   v[i*divY+j][0] = (ratio*(height - (height/divX*i))+top)*cos(j*2*M_PI/divY);
				   v[i*divY+j][1] = height/divX*i;
				   v[i*divY+j][2] = (ratio*(height - (height/divX*i))+top)*sin(j*2*M_PI/divY);
		   }
	}
	// draw the side of the cylinder
	glBegin(GL_QUADS);
	for (i=0; i<divX-1; ++i) {
		   for (j=0; j<divY; ++j) {
				   glVertex3fv(v[i*divY+j]);
				   glVertex3fv(v[i*divY+(j+1)%divY]);
				   glVertex3fv(v[(i+1)*divY+(j+1)%divY]);
				   glVertex3fv(v[(i+1)*divY+j]);
		   }
	}
	glEnd();
	// draw the bottom of the cylinder
	glBegin(GL_POLYGON);
	for (j=0; j<divY; ++j) {
		   i=0;
		   glVertex3fv(v[i*divY+j]);
	}
	glEnd();
	// draw the top of the cylinder
	glBegin(GL_POLYGON);
	for (j=0; j<divY; ++j) {
		   i=divX-1;
		   glVertex3fv(v[i*divY+j]);
	}
	glEnd();
}

void Dog::DrawSixSideTube(float oneSideLength, float height, int divY)
{
	DrawCylinder(oneSideLength, oneSideLength, height, divY, 5);
}

void Dog::DrawCube(float baseSide, float height, int divY)
{
	DrawCylinder(baseSide, baseSide, height, divY, 4);
}


void DrawingHead(Dog d)
{
	glPushMatrix();
	glColor3d(0.8,0.7,0.0);
	glRotated(90, 1.0, 0.0, 0.0);
	d.DrawCylinder(8.0,5.0,25.0,20,3);
	glPopMatrix();
}

void DrawingNeck(Dog d)
{
	glPushMatrix();
	glColor3d(0.8,0.7,0.54);
	glTranslated(0.0, -15.0, 0.0);
	d.DrawCylinder(2.0,2.0,10.0,20,50);
	// d.DrawSixSideTube(5.0, 20.0, 10);
	glPopMatrix();
}

void DrawingBody(Dog d)
{
	glPushMatrix();
	glColor3d(0.8,0.7,0.0);
	glTranslated(0.0, -25.0, -45.0);
	glRotated(90, 1.0, 0.0, 0.0);
	d.DrawCylinder(10.0,10.0,50.0,20,50);
	glPopMatrix();
}

void DrawingLeg(Dog d)
{
	glPushMatrix();
	glColor3d(0.1,0.2,0.5);
	d.DrawSixSideTube(3.0, 15.0, 20);
	glPopMatrix();
}

void DrawingTail(Dog d)
{
	glPushMatrix();
	glColor3d(0.1,0.9,0.5);
	glTranslated(0.0, -15.0, -45.0);
	d.DrawSphere(2.0, 20, 20);
	glPopMatrix();
}

void testDog()
{
	Dog d;
	//d.drawSphere(10.0, 20, 20);
	//d.DrawCylinder(10.0,10.0,20.0,5, 3);

	
	//Draw Four Legs
	glPushMatrix();
	glTranslated(5, -43.0, -5);
	DrawingLeg(d);
	glTranslated(-10, 0.0, 0);
	DrawingLeg(d);
	glTranslated(0.0, 0.0, -35);
	DrawingLeg(d);
	glTranslated(10, 0.0, 0);
	DrawingLeg(d);
	glPopMatrix();
	

	//body, neck,head
	glPushMatrix();
	DrawingBody(d);
	DrawingNeck(d);
	glPushMatrix();
	glRotated(-30, 0.0, 0.0, 1.0);
	DrawingHead(d);
	glPopMatrix();
	//tail
	glPopMatrix();
	DrawingTail(d);
	glPopMatrix();
	
}






//To check of the movment (spinning, moving) is outside the whole animation
//This function is help to check if a ForWard or a BackWard action can be done
bool InsideAnimationScope()
{
	return (spin <= 30.0 && spin >= -30.0) && (position <= 90 && position >= 0);
}

//check if the animation reaches the end
bool ReachTheEnd()
{
	return (spin >= 30.0 || position >= 90);
}

//check if the animation reaches the beginning
bool ReachTheBeginning()
{
	return (spin <= -30.0 || position <= 0);
}

//For the spinning of the tail and head
//Will get called in glutIdleFunc
void spinDisplay(void) 
{
	if(End == 0 && InsideAnimationScope())
	{
		spin = spin + 0.3;
	}
	glutPostRedisplay();
}

//For the movement of the whole dog
//Will get called in glutIdleFunc
void MoveDisplay(void)
{
	if(End == 0 && InsideAnimationScope())
	{
		position = position + 0.5;
	}
	if (position > 90.0)
	{
		glutIdleFunc(NULL);
		cout << "ANIMATION END."<<endl;
		cout << "To see it again, please use R key."<<endl;
		End = 1;
	}
	glutPostRedisplay();
}

//Make spinning and movement of the whole dog at the same time
void Movement(void)
{
	spinDisplay();
	MoveDisplay();
}

//Draw the head
//Using a cube
void drawHead()
{
	glPushMatrix();
	glColor3d(0.51,0.46,0.39);
	glRotatef(spin, 0.0, 1.0, 0.0 );
	glTranslated(0, 0, 11);
	glScaled(10.0, 10.0, 30.0);
	glutSolidCube(1.0);
	glPopMatrix();
}

//Draw the neck
//Using a cube
void drawNeck()
{
	glPushMatrix();
	glTranslated(0.0, -10.0, 0.0);
	glColor3d(0.8,0.7,0.54);
	glScaled(5.0, 10.0, 5.0);
	glutSolidCube(1.0);
	glPopMatrix();
}

//Draw the body of the dog
void DrawBody()
{
	glPushMatrix();
	glColor3d(0.51,0.46,0.39);
	glTranslated(0, -25.0, -15.0);
	glScaled(20.0, 20.0, 50.0);
	glutSolidCube(1.0);
	glPopMatrix();
}

//Draw one leg
//using a cylinder
void DrawLeg(void)
{
	glPushMatrix();
	glColor3d(0.54, 0.54, 0.54);
	glRotated(-90, 1.0, 0.0, 0.0);
	GLUquadricObj * qobj;
	qobj = gluNewQuadric();
	gluQuadricDrawStyle(qobj, GLU_FILL);
	gluCylinder(qobj,rCylinder, rCylinder, 8, 10,10);//0.1
	glPopMatrix();
}

//Draw tail
//Will spin
void DrawTail(void)
{
	glPushMatrix();
	glColor3d(0.5,0.5,0.5);
	glRotatef(spin*2.5, 0.0, 0.0, 1.0 );
	glTranslated(0, 5, 0);
	glScaled(2,10,2);
	glutSolidCube(1.0);
	glPopMatrix();
}

//Draw a dog
//Using different components
void DrawDog()
{
	glPushMatrix();
	//translate to positive Y direction
	//glTranslated(0.0, 30.0, -30.0);
	//move along a line
	

	glPushMatrix();
	//Draw Four Legs
	glTranslated(5, -43.0, 5);
	DrawLeg();
	glTranslated(-10, 0.0, 0);
	DrawLeg();
	glTranslated(0.0, 0.0, -40);
	DrawLeg();
	glTranslated(10, 0.0, 0);
	DrawLeg();
	glPopMatrix();
	
	//draw the body
	DrawBody();
	
	//Draw the tail
	glPushMatrix();
	glTranslated(0, -15, -40);
	DrawTail();
	glPopMatrix();
	
	//Draw the neck
	drawNeck();
	
	//Draw the head
	drawHead();

	glPopMatrix();
}

//Display function
//Used for drawing
void myDisplay(void)
{
	//Clear the screen
	glClear(GL_COLOR_BUFFER_BIT);
	//Draw the dog
	glPushMatrix();
	glTranslated(0.0, 0.0, position);
	//DrawDog();

	glPopMatrix();
	glPushMatrix();
	testDog();
	glPopMatrix();
	//Draw
	glFlush();
}


//Forward the animation by one frame
void Forward()
{
	if (!ReachTheEnd())
	{
		spin = spin + 0.3;
		position = position + 0.5;
	}
	else
		cout << "It is already the end of the animation, you cannot step forward."<<endl;
}

//Backward the animation by one frame
void Backward()
{
	if (!ReachTheBeginning())
	{
		spin = spin - 0.3;
		position = position - 0.5;
	}
	else
		cout << "It is the beginning of the animation, you cannot step backward."<<endl;
}

//myKeyboard function
//key - the key
//x - x coordinate
//y - y coordinate
void myKeyboard(unsigned char key, int x, int y){
	//flip the y value
	// y = SCREEN_HEIGHT - y;
	switch(key){
		//quit
		case 0x1B:
			exit(-1);
		//a	
		case 'f':
			cout << "ONE STEP FORWARD"<<endl;
			glutIdleFunc(NULL);
			Forward();
			glutPostRedisplay();
			break;
		case 'b':
			cout << "ONE STEP BACKWARD"<<endl;
			glutIdleFunc(NULL);
			Backward();
			glutPostRedisplay();
			break;
		case 'r':
			cout << "Rewind"<<endl;
			glutIdleFunc(NULL);
			spin = -30.0;
			position = 0.0;
			glutPostRedisplay();
			break;
		case 'p':
			End = 0;
			glutIdleFunc(Movement);
			break;		
		default:
			break;
	}
}

// myInit
void myInit(void)
{
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glColor3f(0.0f, 0.0f, 0.0f);
	glPointSize(1.0);
	//Set Projection Matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//view frustum
	glOrtho(-80,80,-60,60,-120,250);
	//set modelview matrix
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	//set lookat point
	gluLookAt(4,2.5,4, 0, 0, 0, 0, 1, 0);
	// gluLookAt(4,0,0, 0, 0, 0, 0, 1, 0);
	

}

// main
int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize(800, 600);
	glutInitWindowPosition(10, 10);
	glutCreateWindow("Project 3 -- Zhaochen 'JJ' Liu");
	myInit();
	glViewport(0, 0, 800, 600); 
	glutDisplayFunc(myDisplay);
	glutKeyboardFunc(myKeyboard);
	
	
	
	cout << "Welcome!"<<endl;
	cout << "P - play!"<<endl;
	cout << "F - forward!"<<endl;
	cout << "B - backward!"<<endl;
	cout << "R - rewind!"<<endl;
	
	glutMainLoop();

	return 0;
}
